package oneD.t3dviewer2;

/* Model3D.java  Representation of a 3D model as a wire frame of line
                 segments.

   History:

   12 Jun 2008  If an Exception is encountered while the model
                is being loaded, reset() before throwing or
                rethrowing the exception.
   10 Jun 2008  Extracted from ThreeD.java and JModel3D.java.
 */

import java.io.*;

/**
 * The representation of a 3D model as a wire frame of line segments.
 */
public abstract class Model3D {
	/**
	 * Report problems with data file formatting.
	 * 
	 * @param s
	 *            description of the problem
	 */
	public class FileFormatException extends Exception {
		public FileFormatException(String s) {
			super(s);
		}
	}

	/**
	 * Report problems with adding information to the model.
	 * 
	 * @param s
	 *            description of the problem
	 */
	public class ModelException extends Exception {
		public ModelException(String s) {
			super(s);
		}
	}

	public Model3D() {
	}

	public Model3D(InputStream is) throws IOException, FileFormatException,
			ModelException {
		load(is);
	}

	/**
	 * Reset model to empty state.
	 */
	public abstract void reset();

	/**
	 * Initialize the model geometry from data contained in a stream. If an
	 * exception occurs while the model is being loaded, the model is reset()
	 * back to empty. The stream must fit the following format specification:
	 * 
	 * <pre>
	 *     # comment character
	 *     v x_val y_val z_val
	 *     l p1_val p2_val type
	 * 
	 *     x_val, y_val and z_val are floating-point values.
	 *     p1_val and p2_val are zero-based indexes into the vertex array.
	 *     type is a non-negative integer.  type % 8 == 1 indicates a strut.
	 *     All the "v" specs must precede the "l" specs.
	 * </pre>
	 * 
	 * @param is
	 *            InputStream in the prescribed format.
	 * @exception java.io.IOException
	 *                See exception documentation for reason.
	 * @exception FileFormatException
	 *                See exception documentation for reason.
	 * @exception ModelException
	 *                See exception documentation for reason.
	 */
	public synchronized void load(InputStream is) throws IOException,
			FileFormatException, ModelException {
		reset();
		Reader r = new BufferedReader(new InputStreamReader(is));
		StreamTokenizer st = new StreamTokenizer(r);
		st.eolIsSignificant(true);
		st.commentChar('#');
		try {
			while (st.nextToken() != StreamTokenizer.TT_EOF)
				if (st.ttype == StreamTokenizer.TT_WORD) {
					if ("v".equals(st.sval)) { // v = "vertex"
						// point
						double x = 0, y = 0, z = 0;
						if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
							x = st.nval;
							if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
								y = st.nval;
								if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
									z = st.nval;
								} else
									fileFormatException(st.toString());
							} else
								fileFormatException(st.toString());
						} else
							fileFormatException(st.toString());
						addPoint(x, y, z);
					} else if ("l".equals(st.sval)) { // l = "line"
						// lineSegment
						int p1 = 0, p2 = 0, type = 0;
						if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
							p1 = (int) st.nval;
							if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
								p2 = (int) st.nval;
								if (st.nextToken() == StreamTokenizer.TT_NUMBER) {
									type = (int) st.nval;
								} else
									throw new FileFormatException(st.toString());
							} else
								fileFormatException(st.toString());
						} else
							fileFormatException(st.toString());
						addLineSegment(p1, p2, type);
					}
				} // TT_WORD token
		} catch (IOException e) {
			reset();
			throw e;
		} catch (ModelException e) {
			reset();
			throw e;
		}
		computeBoundingSphere();
	}

	/**
	 * Handle a FileFormatException by emptying the model and then throwing said
	 * exception.
	 * 
	 * @param tokenizerString
	 *            string from tokenizer when bad format encountered
	 * @exception FileFormatException
	 *                See exception documentation for reason.
	 */
	private void fileFormatException(String tokenizerString)
			throws FileFormatException {
		reset();
		throw new FileFormatException(tokenizerString);
	}

	/**
	 * Add a point to the model's geometry.
	 * 
	 * @param x
	 *            x coordinate of point
	 * @param y
	 *            y coordinate of point
	 * @param z
	 *            z coordinate of point
	 */
	abstract void addPoint(double x, double y, double z) throws ModelException;

	/**
	 * Add a LineSegment with endpoints p1 and p2 to the model's geometry.
	 * 
	 * @param p1
	 *            zero-based index of first endpoint
	 * @param p2
	 *            zero-based index of second endpoint
	 * @param type
	 *            type value for this line segment
	 */
	abstract void addLineSegment(int p1, int p2, int type)
			throws ModelException;

	/**
	 * Compute the bounding sphere for the model in its current state.
	 */
	abstract void computeBoundingSphere();
}
